#include "user_utils.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "esp_netif.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "esp_vfs_fat.h"
#include "lwip/inet.h"
#include "lwip/ip4_addr.h"
#include "nvs_flash.h"
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/unistd.h>

#define TAG "utils"

#define NVS_KEY "AIGlass"

#define DEFAULT_WIFI_SSID "1901"
#define DEFAULT_WIFI_PASS "MMMM1901"

static void nvs_read(void);

devInfo_t devInfo = {
    .bat = 100,
    .mediaInfo =
        {
            .volume = 10,
            .state = media_IDLE,
        },
    .wifiInfo =
        {
            .ssid = DEFAULT_WIFI_SSID,
            .password = DEFAULT_WIFI_PASS,
            .enable = false,
            .connState = false,
            .httpServer = false,
        },
};

typedef enum {
    NVS_TYPE_STR = 0,
    NVS_TYPE_INT = 1,
} nvs_write_type_t;

typedef struct {
    char *key;
    char *target;
    int length;
    nvs_write_type_t type;
} nvs_node_t;

nvs_node_t nvs_nodes[] = {
    {"wifi_ssid", devInfo.wifiInfo.ssid, 32, NVS_TYPE_STR},
    {"wifi_pass", devInfo.wifiInfo.password, 64, NVS_TYPE_STR},
    {"bt_addr", devInfo.BTInfo.mac_str, 18, NVS_TYPE_STR},
    {"vol", devInfo.mediaInfo.volume, 4, NVS_TYPE_INT},
};

void update_devInfo() {
    esp_efuse_mac_get_default(devInfo.mac);
    devInfo.SAddr = devInfo.mac[0] + devInfo.mac[1] + devInfo.mac[2];
    devInfo.SAddr += (devInfo.mac[3] + devInfo.mac[4] + devInfo.mac[5]) << 8;
    ESP_LOGI(TAG, "dev mac:%02x:%02x:%02x:%02x:%02x:%02x, saddr:%04x", devInfo.mac[0], devInfo.mac[1], devInfo.mac[2], devInfo.mac[3],
             devInfo.mac[4], devInfo.mac[5], devInfo.SAddr);

    sprintf(devInfo.deviceName, "AIGlass-%04x", devInfo.SAddr);
    sprintf(devInfo.SAddr_str, "%04x", devInfo.SAddr);
    sprintf(devInfo.sn, "%02x:%02x:%02x:%02x:%02x:%02x", devInfo.mac[0], devInfo.mac[1], devInfo.mac[2], devInfo.mac[3], devInfo.mac[4],
            devInfo.mac[5]);

    nvs_read();
}

// 升级固件
void update_firmware(char *path) {
    const esp_partition_t *ota_partition = esp_ota_get_next_update_partition(NULL);
    if (ota_partition == NULL) {
        ESP_LOGE("OTA", "Failed to find OTA partition.");
        return;
    }

    FILE *f = fopen(path, "rb");
    if (f == NULL) {
        ESP_LOGE("OTA", "Failed to open firmware file.");
        return;
    }

    char ota_write_data[1024];
    size_t read_bytes;

    esp_ota_handle_t ota_handle;
    esp_err_t err = esp_ota_begin(ota_partition, OTA_WITH_SEQUENTIAL_WRITES, &ota_handle);
    if (err != ESP_OK) {
        ESP_LOGE("OTA", "esp_ota_begin failed (%s)", esp_err_to_name(err));
        fclose(f);
        return;
    }

    while ((read_bytes = fread(ota_write_data, 1, sizeof(ota_write_data), f)) > 0) {
        err = esp_ota_write(ota_handle, ota_write_data, read_bytes);
        if (err != ESP_OK) {
            ESP_LOGE("OTA", "esp_ota_write failed (%s)", esp_err_to_name(err));
            fclose(f);
            esp_ota_end(ota_handle);
            return;
        }
    }

    fclose(f);

    if (esp_ota_end(ota_handle) != ESP_OK) {
        ESP_LOGE("OTA", "esp_ota_end failed!");
        return;
    }

    err = esp_ota_set_boot_partition(ota_partition);
    if (err != ESP_OK) {
        ESP_LOGE("OTA", "esp_ota_set_boot_partition failed (%s)", esp_err_to_name(err));
        return;
    }

    ESP_LOGI("OTA", "OTA successful, delete ota file...");
    unlink(path);

    ESP_LOGI("OTA", "OTA update successful, restarting...");
    esp_restart();
}

// nvs存储
esp_err_t nvs_write(char *key, char *value) {
    if (strlen(key) > 20 || strlen(value) > 64) {
        ESP_LOGE(TAG, "key or value too long");
        return ESP_FAIL;
    }

    nvs_handle_t nvs_handle;
    esp_err_t err = nvs_open(NVS_KEY, NVS_READWRITE, &nvs_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return ESP_FAIL;
    }

    err = nvs_set_str(nvs_handle, key, value);
    if (err != ESP_OK) {
        nvs_close(nvs_handle);
        return ESP_FAIL;
    }

    err = nvs_commit(nvs_handle);
    nvs_close(nvs_handle);
    return err;
}

static void nvs_read() {
    nvs_handle_t nvs_handle;
    esp_err_t err = nvs_open(NVS_KEY, NVS_READWRITE, &nvs_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return;
    }

    size_t value_length = 32;
    err = nvs_get_str(nvs_handle, "wifi_ssid", devInfo.wifiInfo.ssid, &value_length);
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "nvs get wifi ssid failed, use default: %s", DEFAULT_WIFI_SSID);
    } else {
        ESP_LOGI(TAG, "nvs get wifi ssid: %s", devInfo.wifiInfo.ssid);
    }

    value_length = 64;
    err = nvs_get_str(nvs_handle, "wifi_pass", devInfo.wifiInfo.password, &value_length);
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "nvs get wifi pass failed, use default: %s", DEFAULT_WIFI_PASS);
    } else {
        ESP_LOGI(TAG, "nvs get wifi pass: %s", devInfo.wifiInfo.password);
    }

    value_length = 18;
    err = nvs_get_str(nvs_handle, "bt_addr", devInfo.BTInfo.mac_str, &value_length);
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "nvs get bt addr failed, use default: %s", devInfo.BTInfo.mac_str);
    } else {
        ESP_LOGI(TAG, "nvs get bt addr: %s", devInfo.BTInfo.mac_str);
        sscanf(devInfo.BTInfo.mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &devInfo.BTInfo.mac[0], &devInfo.BTInfo.mac[1],
               &devInfo.BTInfo.mac[2], &devInfo.BTInfo.mac[3], &devInfo.BTInfo.mac[4], &devInfo.BTInfo.mac[5]);
    }

    nvs_close(nvs_handle);

    return;
}

char *get_ip_address(void) {
    static char ip_str[16] = {0}; // static 保证返回后仍有效
    esp_netif_ip_info_t ip_info;
    esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); // 获取 STA 接口

    if (netif == NULL) {
        ESP_LOGE("IP", "Failed to get netif handle");
        return NULL;
    }

    if (esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) {
        // 使用 IP2STR 宏来获取 IP 地址字符串
        snprintf(ip_str, sizeof(ip_str), IPSTR, IP2STR(&ip_info.ip));
        return ip_str;
    }

    return NULL;
}